
This tutorial uses data published by NASA:
Hat tip to https://fosstodon.org/@65dBnoise/108251277108722231 for providing the pointers
Known issues:
import pandas as pd
from geopandas import GeoDataFrame, read_file
from shapely.geometry import Point, LineString, Polygon
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import movingpandas as mpd
from os.path import exists
from urllib.request import urlretrieve
import warnings
warnings.simplefilter("ignore")
INFO: Missing optional dependencies. To use the trajectory smoother classes please install Stone Soup (see https://stonesoup.readthedocs.io/en/latest/#installation).
hvplot_defaults = {'tiles':None, 'frame_height':400, 'frame_width':700, 'cmap':'Viridis', 'colorbar':True}
mpd.__version__
'0.9.rc3'
"The car-sized Perseverance and its little helicopter buddy Ingenuity landed together inside Mars' Jezero Crater on Feb. 18." https://www.space.com/perseverance-rover-100-mars-days (by Mike Wall published June 02, 2021)
"One sol lasts about 24 hours and 40 minutes, slightly longer than an Earth day." https://www.space.com/perseverance-rover-100-mars-days
def to_timestamp(row):
start_time = datetime(2021,2,18,0,0,0) # sol 0
try:
sol = row['sol'] # rover
except KeyError:
sol = row['Sol'] # heli
td = timedelta(hours=24*sol, minutes=40*sol)
return start_time + td
def get_df_from_url(url):
file = url.split('/')[-1]
if not exists(file):
urlretrieve(url, file)
gdf = read_file(file)
gdf['time'] = gdf.apply(to_timestamp, axis=1)
gdf.set_index('time', inplace=True)
return gdf
m20_waypoints_json = "https://mars.nasa.gov/mmgis-maps/M20/Layers/json/M20_waypoints.json"
heli_waypoints_json = "https://mars.nasa.gov/mmgis-maps/M20/Layers/json/m20_heli_waypoints.json"
m20_df = get_df_from_url(m20_waypoints_json)
heli_df = get_df_from_url(heli_waypoints_json)
print(f'M20 records: {len(m20_df)}')
print(f'Heli records: {len(heli_df)}')
M20 records: 156 Heli records: 29
m20_df.describe()
| site | drive | sol | SCLK_START | SCLK_END | easting | northing | elev_geoid | elev_radii | radius | ... | roll | pitch | yaw | yaw_rad | tilt | dist_m | dist_total | dist_km | dist_mi | ORIG_FID | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 156.000000 | 156.000000 | 156.000000 | 1.560000e+02 | 1.560000e+02 | 1.560000e+02 | 1.560000e+02 | 156.000000 | 156.000000 | 1.560000e+02 | ... | 156.000000 | 156.000000 | 156.000000 | 156.000000 | 156.000000 | 156.000000 | 156.000000 | 156.000000 | 156.000000 | 156.000000 |
| mean | 10.705128 | 1635.076923 | 261.012821 | 6.858288e+08 | 6.858989e+08 | 4.353919e+06 | 1.093211e+06 | -2565.319896 | -4248.434796 | 3.391942e+06 | ... | 0.078588 | 0.877516 | -29.754424 | -0.519312 | 3.247422 | 73.370897 | 3369.154237 | 3.369103 | 2.093141 | 83.500000 |
| std | 7.343391 | 1757.848735 | 141.790576 | 5.665063e+07 | 5.664626e+07 | 9.786968e+02 | 6.396507e+02 | 14.163160 | 13.179175 | 1.317897e+01 | ... | 3.123551 | 3.221801 | 106.107954 | 1.851936 | 3.148950 | 84.848770 | 3668.565027 | 3.668456 | 2.279536 | 45.177428 |
| min | 3.000000 | 0.000000 | 13.000000 | 0.000000e+00 | 0.000000e+00 | 4.351710e+06 | 1.092270e+06 | -2585.869629 | -4266.522461 | 3.391923e+06 | ... | -12.377935 | -7.797700 | -179.475000 | -3.132400 | -0.043088 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 6.000000 |
| 25% | 4.000000 | 410.000000 | 126.750000 | 6.781907e+08 | 6.781920e+08 | 4.353956e+06 | 1.092629e+06 | -2574.016500 | -4255.397319 | 3.391935e+06 | ... | -1.075334 | -0.520792 | -126.751645 | -2.212250 | 1.087888 | 8.878500 | 302.875000 | 0.305000 | 0.187500 | 44.750000 |
| 50% | 9.000000 | 1304.000000 | 330.500000 | 6.962873e+08 | 6.962881e+08 | 4.354411e+06 | 1.093255e+06 | -2569.165000 | -4252.637510 | 3.391937e+06 | ... | -0.120000 | 0.531261 | -58.070700 | -1.013550 | 1.995700 | 36.329000 | 2117.274000 | 2.120000 | 1.315000 | 83.500000 |
| 75% | 17.250000 | 2247.000000 | 395.250000 | 7.020252e+08 | 7.020356e+08 | 4.354547e+06 | 1.093694e+06 | -2558.318000 | -4245.486393 | 3.391945e+06 | ... | 1.179550 | 2.214195 | 45.275105 | 0.790200 | 4.457050 | 111.595500 | 5586.892750 | 5.587500 | 3.475000 | 122.250000 |
| max | 24.000000 | 9436.000000 | 437.000000 | 7.057373e+08 | 7.057424e+08 | 4.355183e+06 | 1.094703e+06 | -2520.911000 | -4204.339000 | 3.391986e+06 | ... | 13.177640 | 19.349400 | 179.893300 | 3.139700 | 19.833200 | 313.832000 | 11445.860000 | 11.450000 | 7.110000 | 161.000000 |
8 rows × 22 columns
m20_df.hvplot(title="M20 & heli waypoints", hover_cols=['sol'], **hvplot_defaults) * heli_df.hvplot()
m20_traj = mpd.Trajectory(m20_df, 'm20')
heli_traj = mpd.Trajectory(heli_df, 'heli')
m20_traj.hvplot(title="M20 & heli trajectories", line_width=3, **hvplot_defaults) * heli_traj.hvplot(line_width=3, **hvplot_defaults)
m20_traj.hvplot(title="Rover speed (only suitable for relative comparison)",
c='speed', line_width=7, **hvplot_defaults)
Status: still looking for suitable background maps, since OpenPlanetary maps don't go into enough detail, e.g.
from geoviews.element import WMTS
layer = 'celestia_mars-shaded-16k_global'
MarsImagery = WMTS(
'http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/'+layer+'/{Z}/{X}/{Y}.png',
name="Mars")
MarsImagery
m20_traj.hvplot(title="M20 & heli trajectories", tiles=MarsImagery) * heli_traj.hvplot(**hvplot_defaults)